package net.aiion.weave.impl.interception;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

import net.aiion.weave.spi.interception.Interceptable;
import net.aiion.weave.spi.interception.InterceptionContext;
import net.aiion.weave.spi.interception.InterceptionException;
import net.aiion.weave.spi.interception.Interceptor;
import net.aiion.weave.spi.naming.Context;

public class InterceptionContextImpl implements InterceptionContext
{
    private final Context namingContext;

    private final Iterator<Interceptor> interceptorIterator;

    private final Interceptable interceptable;

    private final Method interceptedMethod;
    private Object[] methodArguments;

    public InterceptionContextImpl(final Context aNamingContext, final Collection<Interceptor> aInterceptors,
        final Interceptable aInterceptable, final Method aMethod, final Object[] aMethodArguments)
    {
        namingContext = aNamingContext;
        interceptorIterator = aInterceptors.iterator();
        interceptable = aInterceptable;
        interceptedMethod = aMethod;
        methodArguments = Arrays.copyOf(aMethodArguments, aMethodArguments.length);
    }

    @Override
    public Context getNamingContext() {
        return namingContext;
    }

    @Override
    public Interceptable getInterceptable() {
        return interceptable;
    }

    @Override
    public Method getInterceptedMethod() {
        return interceptedMethod;
    }

    @Override
    public Object[] getMethodArguments() {
        return methodArguments;
    }

    @Override
    public void setMethodArguments(final Object[] aMethodArguments) {
        methodArguments = Arrays.copyOf(aMethodArguments, aMethodArguments.length);
    }

    @Override
    public Object forwardInterception() {
        if (interceptorIterator.hasNext()) {
            return interceptorIterator.next().interceptInvocation(this);
        }

        try {
            return interceptedMethod.invoke(interceptable, methodArguments);
        } catch (final IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            throw new InterceptionException(e);
        }
    }
}
